package com.yswy.common.utils;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Random;

public class DoudouBase64 {
    public static String normalKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    public static String normalWebSafeKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";

    /**
     * 根据一个原有的base64编码表字符串打乱其字符顺序，从而获得一个新的随机的base64编码表
     * @param key
     * @return
     */
    public static String generateRadomKey(String key){
        ArrayList<Character> chars=new ArrayList<>();
        for (int i = 0; i < key.length(); i++) {
            chars.add(key.charAt(i));
        }

        StringBuilder sb=new StringBuilder();
        while(chars.size()>0){
            int index=new Random().nextInt(chars.size());
            sb.append(chars.remove(index));
        }
        return sb.toString();
    }
    //编码字典
    private byte[] intToBase64;
    //解码字典
    private byte[] base64ToInt;
    public DoudouBase64(String mappingKey) {
        init(mappingKey);
    }
    public DoudouBase64() {
        init(normalKey);
    }

    /**
     * 根据一个65位长度的ASCII字符串(不包含中文,最后一位用来补齐)初始化编码和解码字典
     *
     * @param mappingKey
     */
    private void init(String mappingKey) {
        System.out.println("初始化mappingKey:" + mappingKey);
        // 生成编码mapping
        intToBase64 = mappingKey.getBytes();

        // 生成解码mapping
        base64ToInt = new byte[255];
        for (int i = 0; i < base64ToInt.length; i++) {
            base64ToInt[i] = -1;
        }
        for (int i = 0; i < intToBase64.length; i++) {
            base64ToInt[intToBase64[i]] = (byte) i;
        }
    }

    /**
     * 编码方法
     *
     * @param a
     * @return
     */
    public byte[] encode(byte[] a) {
        int aLen = a.length; // 总长度
        int numFullGroups = aLen / 3; // 以3个byte组成以4个字符为一组的组数
        int numBytesInPartialGroup = aLen - 3 * numFullGroups; // 余数
        int resultLen = 4 * ((aLen + 2) / 3); // 输出长度总是4倍数，如果有余数，(aLen+2)/3保证将余数包含，并有空间放置填充符=

        byte[] result = new byte[resultLen];
        int rCursor = 0;

        int inCursor = 0;
        for (int i = 0; i < numFullGroups; i++) {
            int byte0 = a[inCursor++] & 0xff;
            int byte1 = a[inCursor++] & 0xff;
            int byte2 = a[inCursor++] & 0xff;
            result[rCursor++] = intToBase64[byte0 >> 2];
            result[rCursor++] = intToBase64[(byte0 << 4) & 0x3f | (byte1 >> 4)];
            result[rCursor++] = intToBase64[(byte1 << 2) & 0x3f | (byte2 >> 6)];
            result[rCursor++] = intToBase64[byte2 & 0x3f];
        }
        // 处理余数
        if (numBytesInPartialGroup != 0) {
            int byte0 = a[inCursor++] & 0xff;
            result[rCursor++] = intToBase64[byte0 >> 2];
            // 余数为1
            if (numBytesInPartialGroup == 1) {
                result[rCursor++] = intToBase64[(byte0 << 4) & 0x3f];
                result[rCursor++] = intToBase64[intToBase64.length - 1];
                result[rCursor++] = intToBase64[intToBase64.length - 1];
            } else {
                // 余数为2
                int byte1 = a[inCursor++] & 0xff;
                result[rCursor++] = intToBase64[(byte0 << 4) & 0x3f | (byte1 >> 4)];
                result[rCursor++] = intToBase64[(byte1 << 2) & 0x3f];
                result[rCursor++] = intToBase64[intToBase64.length - 1];
            }
        }
        return result;
    }

    /**
     * 编码 并返回指定编码字符串
     *
     * @param s
     * @return
     */
    public String encode2String(byte[] s, String charsetName) {
        try {
            return new String(encode(s), charsetName);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 编码 并返回utf8编码字符串
     *
     * @param s
     * @return
     */
    public String encode2String(byte[] s) {
        return encode2String(s,"utf8");
    }

    /**
     * 解码并返回指定编码字符串
     *
     * @param s
     * @return
     */
    public String decode2String(byte[] s, String charsetName) {
        try {
            return new String(decode(s), charsetName);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 解码并返回utf8编码字符串
     *
     * @param s
     * @return
     */
    public String decode2String(byte[] s) {
        return decode2String(s,"utf8");
    }

    public byte[] decode(byte[] s) {
        // 字符总长必须是4的倍数
        int sLen = s.length;
        int numGroups = sLen / 4;
        if (4 * numGroups != sLen)
            throw new IllegalArgumentException("字串长度必须是4的倍数:" + sLen);
        // 余1个byte则算漏了两个byte，余2个byte则算漏掉了1个byte
        int missingBytesInLastGroup = 0;
        int numFullGroups = numGroups;
        if (sLen != 0) {
            // 余2个byte的情况
            if (s[sLen - 1] == intToBase64[intToBase64.length - 1]) {
                missingBytesInLastGroup++;
                // 如果有余数发生，则完整3个byte组数少一个。
                numFullGroups--;
            }
            // 余1个byte的情况
            if (s[sLen - 2] == intToBase64[intToBase64.length - 1])
                missingBytesInLastGroup++;
        }
        // 总字节长度
        byte[] result = new byte[3 * numGroups - missingBytesInLastGroup];

        int inCursor = 0, outCursor = 0;
        for (int i = 0; i < numFullGroups; i++) {

            int ch0 = base64ToInt[s[inCursor++]];
            int ch1 = base64ToInt[s[inCursor++]];
            int ch2 = base64ToInt[s[inCursor++]];
            int ch3 = base64ToInt[s[inCursor++]];
            result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
            result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
            result[outCursor++] = (byte) ((ch2 << 6) | ch3);
        }
        if (missingBytesInLastGroup != 0) {
            int ch0 = base64ToInt[s[inCursor++]];
            int ch1 = base64ToInt[s[inCursor++]];
            // 不管余1还是余2个byte，肯定要解码一个byte。
            result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));

            // 如果余2个，即差一个才构成3byte，那么还要解码第二个byte。
            if (missingBytesInLastGroup == 1) {
                int ch2 = base64ToInt[s[inCursor++]];
                result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
            }
        }
        return result;
    }
}